import { pronunciationConfigAtom } from '@/store'
import type { PronunciationType } from '@/typings'
import { addHowlListener } from '@/utils'
import { romajiToHiragana } from '@/utils/kana'
import noop from '@/utils/noop'
import type { Howl } from 'howler'
import { useAtomValue } from 'jotai'
import { useEffect, useMemo, useState } from 'react'
import useSound from 'use-sound'
import type { HookOptions } from 'use-sound/dist/types'

const pronunciationApi = 'https://dict.youdao.com/dictvoice?audio=' // 有道翻译的发音 API
export function generateWordSoundSrc(word: string, pronunciation: Exclude<PronunciationType, false>): string {
  // 生成单词发音的 URL
  switch (pronunciation) {
    case 'uk':
      return `${pronunciationApi}${word}&type=1` // 有道 API 中 type=1 代表英式发音
    case 'us':
      return `${pronunciationApi}${word}&type=2`
    case 'romaji':
      return `${pronunciationApi}${romajiToHiragana(word)}&le=jap`
    case 'zh':
      return `${pronunciationApi}${word}&le=zh`
    case 'ja':
      return `${pronunciationApi}${word}&le=jap`
    case 'de':
      return `${pronunciationApi}${word}&le=de`
    case 'hapin':
    case 'kk':
      return `${pronunciationApi}${word}&le=ru` // 有道不支持哈萨克语, 暂时用俄语发音兜底
    case 'id':
      return `${pronunciationApi}${word}&le=id`
    default:
      return ''
  }
}

export default function usePronunciationSound(word: string, isLoop?: boolean) {
  const pronunciationConfig = useAtomValue(pronunciationConfigAtom) // 发音配置
  const loop = useMemo(() => (typeof isLoop === 'boolean' ? isLoop : pronunciationConfig.isLoop), [isLoop, pronunciationConfig.isLoop]) // 是否循环播放
  const [isPlaying, setIsPlaying] = useState(false) // 播放状态

  const [play, { stop, sound }] = useSound(generateWordSoundSrc(word, pronunciationConfig.type), {
    html5: true,
    format: ['mp3'],
    loop,
    volume: pronunciationConfig.volume, // 音量
    rate: pronunciationConfig.rate, // 播放速率
  } as HookOptions) // 播放和停止函数，以及声音对象

  useEffect(() => {
    if (!sound) return //
    sound.loop(loop) // 更新声音的循环属性
    return noop // 清理函数
  }, [loop, sound]) // 当声音对象或循环设置改变时，更新声音的循环属性

  useEffect(() => {
    if (!sound) return
    const unListens: Array<() => void> = []

    unListens.push(addHowlListener(sound, 'play', () => setIsPlaying(true))) // 播放时设置播放状态为 true
    unListens.push(addHowlListener(sound, 'end', () => setIsPlaying(false))) // 播放结束时设置播放状态为 false
    unListens.push(addHowlListener(sound, 'pause', () => setIsPlaying(false))) // 暂停时设置播放状态为 false
    unListens.push(addHowlListener(sound, 'playerror', () => setIsPlaying(false))) /// 播放错误时设置播放状态为 false

    return () => {
      setIsPlaying(false) // 清理时设置播放状态为 false
      unListens.forEach((unListen) => unListen()) // 移除所有事件监听器
      ;(sound as Howl).unload() // 卸载声音对象
    }
  }, [sound]) // 当声音对象改变时，添加和移除事件监听器

  return { play, stop, isPlaying }
}

export function usePrefetchPronunciationSound(word: string | undefined) {
  // 预加载单词发音
  const pronunciationConfig = useAtomValue(pronunciationConfigAtom)

  useEffect(() => {
    // 预加载单词发音
    if (!word) return //   没有单词则不进行预加载

    const soundUrl = generateWordSoundSrc(word, pronunciationConfig.type) // 生成发音 URL
    if (soundUrl === '') return //  URL 为空则不进行预加载

    const head = document.head // 获取文档头部
    const isPrefetch = (Array.from(head.querySelectorAll('link[href]')) as HTMLLinkElement[]).some((el) => el.href === soundUrl) // 检查是否已经存在预加载链接

    if (!isPrefetch) {
      // 如果没有预加载链接，则创建一个新的链接元素进行预加载
      const audio = new Audio() // 创建一个新的音频元素
      audio.src = soundUrl // 设置音频源
      audio.preload = 'auto' // 设置预加载属性

      // gpt 说这这两行能尽可能规避下载插件被触发问题。 本地测试不加也可以，考虑到别的插件可能有问题，所以加上保险
      audio.crossOrigin = 'anonymous' // 设置跨域属性
      audio.style.display = 'none' // 隐藏音频元素

      head.appendChild(audio) // 将音频元素添加到文档头部

      return () => {
        head.removeChild(audio)
      } // 清理函数，移除预加载的音频元素
    }
  }, [pronunciationConfig.type, word]) // 当单词或发音类型改变时，重新进行预加载
}
